#include <GL/glew.h>
#include <GL/freeglut.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform2.hpp>
#include <glm/gtx/projection.hpp>

#include "myWindow.h"

#include <iostream>
#include <fstream>
#include <vector>
#define MOUSE_LEFT_BUTTON 0


const char* loadShaderAsString(const char* file)
{
    std::ifstream shader_file(file, std::ifstream::in);
    std::string str((std::istreambuf_iterator<char>(shader_file)), std::istreambuf_iterator<char>());
    return str.c_str();
}



void myWindow::OnRender(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //////////////////////////////////////
//    // View and Projection Matrices Setting
//    //
    // Projection Matrix
    glm::mat4 projection_matrix = glm::mat4 ( 1.0f );
    projection_matrix = glm::infinitePerspective( 52.0f , (float)this->width / (float)this->height, 0.1f);
//
//    glUseProgram( this->programHandle );
//////
//////    // Bind View Matrix
//    this->camara->bindViewCamara(this->programHandle);
//////
//////    // Bind Projection MAtrix
//    GLuint location_projection_matrix = glGetUniformLocation( this->programHandle, "ProjectionMatrix");
//    if( location_projection_matrix >= 0 )
//	{
//		glUniformMatrix4fv( location_projection_matrix, 1, GL_FALSE, &projection_matrix[0][0]);
//	}
//////    //
//////    ///////////////////////////////////////////
//////
//////
//////
//////    //////////////////////////////////////
//////    // Bind Light Settings
//    glm::vec4 light_position = glm::vec4( 180.0f, 180.0f, 180.0f, 1.0f );
//    glm::vec3 light_intensity = glm::vec3( 1.0f, 1.0f, 1.0f );
//
//    GLuint location_light_position = glGetUniformLocation( this->programHandle, "LightPosition");
//    if( location_light_position >= 0 )
//	{
//        glUniform4fv( location_light_position, 1, &light_position[0]);
//	}
//
//    GLuint location_light_intensity = glGetUniformLocation( this->programHandle, "Ld");
//    if( location_light_intensity >= 0 )
//	{
//		glUniform3fv( location_light_intensity, 1, &light_intensity[0]);
//	}
    //
    ///////////////////////////////////////////



//////////////////////// renderizados ///////////////////77
//    this->hoyo1->setCam(camara, projection_matrix);
//    this->hoyo2->setCam(camara, projection_matrix);
//    this->suelo->setCam(camara, projection_matrix);
//    this->alga1->setCam(camara, projection_matrix);
//    this->alga2->setCam(camara, projection_matrix);
//    this->cang->setCam(camara, projection_matrix);
//    this->pez1->setCam(camara, projection_matrix);
    for (unsigned int i = 0; i < this->listaRenders.size(); i++) this->listaRenders[i]->setCam(camara, projection_matrix);
    for (unsigned int i = 0; i < this->listaRenders.size(); i++) this->listaRenders[i]->render(this->programHandle);
//    hoyo1->render(this->programHandle);
//    hoyo2->render(this->programHandle);
//    suelo->render(this->programHandle);
//    alga1->render(this->programHandle);
//    alga2->render(2);
//    cang->render(2);
//    pez1->render(2);
//    cang->render(this->programHandle);
//    glm::mat4 modelo = glm::mat4(1.0f);

//    changeObjectColor(this->programHandle,1.0,0.0,0.0);
//    modelo = glm::translate(modelo, glm::vec3(1.0,1.0,8.0));
//    	modelo = glm::rotate(modelo, 90.0f,glm::vec3(1.0f,0,0));
//    	modelo = glm::scale(modelo, glm::vec3(1.0,1.0,4.0));
//    far->render(this->programHandle,&modelo);
//    glm::mat4 modelo2 = glm::mat4(1.0f);
//
// //    changeObjectColor(this->programHandle,1.0,0.0,0.0);
//     modelo2 = glm::translate(modelo2, glm::vec3(1.0,1.0,8.0));
//     	modelo2 = glm::rotate(modelo2, -90.0f,glm::vec3(1.0f,0,0));
////     	modelo2 = glm::rotate(modelo2, -90.0f,glm::vec3(0.0f,0.0,0.0));
//     	modelo2 = glm::scale(modelo2, glm::vec3(1.0,-1.0,4.0));
//     fab->render(this->programHandle,&modelo2);
//    cang3->render(this->programHandle);
//    piedra1->render(this->programHandle);
//    piedra2->render(this->programHandle);
//    piedra3->render(this->programHandle);
//    piedra4->render(this->programHandle);
//    piedra5->render(this->programHandle);
//    alga1->render(this->programHandle);
//    alga2->render(this->programHandle);
//    alga3->render(this->programHandle);
//    alga5->render(this->programHandle);
//    alga4->render(this->programHandle);
//    suelo->render(this->programHandle);
//    pez2->render(this->programHandle);
//    pez3->render(this->programHandle);





    ///////////// movimientos ///////////////
    if (mover){
    cang3->moverAlaIzquierda();
    cang->moverAlaIzquierda();
	pez1->moverPez();
	pez2->moverPez();
	cang2->moverAlaIzquierda();
	pez3->moverPez();
	alga1->mover();
	alga2->mover();
	alga3->mover();
	alga5->mover();
	alga4->mover();
    }
	camara->rotarCamaraEstatica();



	this->Repaint();


    glutSwapBuffers();
}

void  myWindow::OnIdle()
{
}

myWindow::myWindow(){
	this->cameraDragging = false;
	this->dx = 0;
	this->dy = 0;
    this->fragShader = 0;
    this->programHandle = 0;
    this->vertShader = 0;
    this->camara = NULL;
    this->cuerpoCangrejo = NULL;
}

// When OnInit is called, a render context (in this case GLUT-Window)
// is already available!
void  myWindow::OnInit()
{

	mover = true;
	cargador =new  CargadorDeTextures(20);
	sF = new ShaderFactory();
    /////////////// Crear aca los elementos de la escena //////////////
//	int handle1 = sF->crearShader("src/TexturingAndDiffuseShadingVShader.vert", "src/TexturingAndDiffuseShadingFShader.frag" );
    far = new FormaHoyos();
    fab = new FormaHoyos();

//    this->listaRenders.push_back(new Piso(20));
    this->camara = new Camara();
    hoyoPiedra1 = new Hoyo(glm::vec3(0.0f, 10.0f, 0.0f), glm::vec3(0.2f, 0.2f, 0.2f));
    hoyoPiedra1->cambiarColor(glm::vec3(1.0f,1.0f, 1.0f));
    hoyo1 = new Hoyo(glm::vec3(-35.0, 8.0, -11.0), glm::vec3(3.0f, 3.0f, 3.0f));
    hoyo2 = new Hoyo(glm::vec3(-100.0f, -120.0f, 0.0f), glm::vec3(5.0f, 5.0f, 5.0f));
    hoyo3 = new Hoyo(glm::vec3(45.0f, -40.0f, 0.0f), glm::vec3(3.0f, 4.0f, 6.0f));
    hoyo4 = new Hoyo(glm::vec3(-140.0f, 53.0f, 0.0f), glm::vec3(7.0f, 9.0f, 7.0f));
    hoyo5 = new Hoyo(glm::vec3(-50.0f, 70.0f, 0.0f), glm::vec3(9.0f, 4.0f, 5.0f));
    aleta = new FormaAleta();
    piedra4 = new Piedra(glm::vec3(-24.0, 20.0, 3.0));
    pez1 = new Pez(glm::vec3 (-10.5, -15.0, 12.0), glm::vec3 (1.0, 1.0, 1.0), glm::vec3(0.0, 0.0, 1.0), -90);
    cang =  new Cangrejo(glm::vec3(-4.0, -20.0,4.5f), glm::vec3(1.0f, -1.0f, 1.00f), glm::vec3(0.0, 0.0, 1.0), 40);
    cang->setearPasoInicial(75.0f);
    cang2 =  new Cangrejo(glm::vec3(-40.0, 15.0,4.5f), glm::vec3(1.0f, -1.0f, 1.00f), glm::vec3(0.0, 0.0, 1.0), 120);
    cang3 = new Cangrejo(glm::vec3(22.0, 20.0,4.5f), glm::vec3(-1.0f, 1.0f, 1.00f), glm::vec3(0.0, 0.0, 1.0), 120);
    cang2->setearPasoInicial(145.0f);
    cang3->setearPasoInicial(33.0f);

    pez2 = new Pez(glm::vec3 (10.5, 15.0, 15.0), glm::vec3 (1.0, 1.0, 1.0), glm::vec3(0.0, 0.0, 1.0), 90);
    pez2->setearDireccion();
    pez2->setearVelocidad(0.5f);
//    pez3->setearVelocidad(0.35f);
    pez3 = new Pez(glm::vec3 (-30.5, -40.0, 30.0), glm::vec3 (1.0, 1.0, 0.75), glm::vec3(0.0, 0.0, 1.0), -90);
    pez3->setearVelocidad(0.35f);
    alga1 = new Alga(glm::vec3(-15.0, 8.0, -11.0), glm::vec3(1.0, 1.0, 3.0), glm::vec3(1.0, 0.0, 1.0), 5.0);
//    alga1->setearPosInicial(0.45f);
    alga2 = new Alga(glm::vec3(-15.0, 8.0, -11.0), glm::vec3(1.0, 1.0, 3.0), glm::vec3(1.0, 0.0, 1.0), -5.0);
//    alga2->setearPosInicial(-2.2f);
    alga3 = new Alga(glm::vec3(-15.0, 8.0, -11.0), glm::vec3(1.0, 2.0, 3.5), glm::vec3(1.0, 1.0, 1.0), 45.0);
//    alga3->setearPosInicial(1.5f);
    alga5 = new Alga(glm::vec3(-15.0, 8.0, -11.0), glm::vec3(1.0, 1.0, 3.3), glm::vec3(1.0, 1.0, -1.0), 0.0);
//    alga5->setearPosInicial(-1.0f);
    alga4 = new Alga(glm::vec3(-40.0, 25.0, -7.0), glm::vec3(1.0, 1.0, 1.0), glm::vec3(1.0, 1.0, 1.0), 15.0);
    suelo = new Superficie();
    techo = new Superficie();
    techo->setAncho(10000.0, 400.0);


    suelo->setTextureid(cargador->cargarImagen("arena.jpg"));
    int texturaPiedra = cargador->cargarImagen("mapaNormales2.jpg");
    int numero = cargador->cargarImagen("roca.jpg");

    techo->setTextureid(cargador->cargarImagen("superficieAgua.jpg"));
    hoyo1->setTextureid(numero, texturaPiedra);
    hoyo2->setTextureid(numero, texturaPiedra);
    hoyo3->setTextureid(numero, texturaPiedra);
    hoyo4->setTextureid(numero, texturaPiedra);
    hoyo5->setTextureid(numero, texturaPiedra);
    far->setTextureid(numero);
    fab->setTextureid(numero);
    numero = cargador->cargarImagen("pezFede.jpg");
//    int numeroaleta=cargador->cargarImagen("escamas2.jpg");
    int cangrejoNormalId = cargador->cargarImagen("mapaNormales3.jpg");
    int reflexion = cargador->cargarImagen("superficieAgua.jpg");
//    int pezNormalId = 0 ;
    int pezNormalId = cargador->cargarImagen("pezFedeNormal.png");
    int aletaNormalId = cargador->cargarImagen("mapaAletas.png");
    pez1->setTextureid(numero, numero,pezNormalId , aletaNormalId, reflexion);
    pez2->setTextureid(numero, numero,pezNormalId , aletaNormalId, reflexion);
    pez3->setTextureid(numero, numero,pezNormalId , aletaNormalId, reflexion);
    int numeroCuerpoCangrejo = cargador->cargarImagen("naranja.jpg");
    int numeroOjosCangrejo = cargador->cargarImagen("ojo.jpg");
    cang->setFixtureId(numeroCuerpoCangrejo, numeroCuerpoCangrejo,numeroOjosCangrejo, cangrejoNormalId);
    cang2->setFixtureId(numeroCuerpoCangrejo,numeroCuerpoCangrejo,numeroOjosCangrejo, cangrejoNormalId);
    cang3->setFixtureId(numeroCuerpoCangrejo,numeroCuerpoCangrejo,numeroOjosCangrejo, cangrejoNormalId);
    int numeroAlgas  = cargador->cargarImagen("planta2.png");
    alga1->setTextureid(numeroAlgas);
    alga2->setTextureid(numeroAlgas);
    alga3->setTextureid(numeroAlgas);
    alga4->setTextureid(numeroAlgas);
    alga5->setTextureid(numeroAlgas);
    suelo->setTextureNormalId(cangrejoNormalId);
    this->listaRenders.push_back(suelo);
    this->listaRenders.push_back(hoyo1);
    this->listaRenders.push_back(hoyo2);
//    this->listaRenders.push_back(hoyo3);
    this->listaRenders.push_back(hoyo4);
    this->listaRenders.push_back(pez2);
    this->listaRenders.push_back(pez3);
//    this->listaRenders.push_back(pez1);
    this->listaRenders.push_back(cang);

//    this->listaRenders.push_back(cang2);
//    this->listaRenders.push_back(cang3);
//
//
    this->listaRenders.push_back(alga1);
    this->listaRenders.push_back(alga2);
//    this->listaRenders.push_back(alga3);
    this->listaRenders.push_back(alga5);
//    this->listaRenders.push_back(alga4);
    this->listaRenders.push_back(techo);

	glClearColor(0.0f, 0.15f, 0.3f, 0.0f);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE); //Preguntar si esto esta bien

	int nuevo = sF->crearShader("src/prueba2NormalShader.vert", "src/prueba2NormalShader.frag" );
//	int handlerNuevo = sF->crearShader("src/NORMALSHAD.vert", "src/NORMALSHAD.frag" );
	int handlerPlanta = sF->crearShader("src/HojaPlantaVShader.vert", "src/HojaPlantaFShader.frag" );
	int handlerWave = sF->crearShader("src/Wave.vert", "src/Wave.frag" );
	int handlerPhong =  sF->crearShader("src/HojaPlantaVShader.vert", "src/HojaPlantaFShader.frag");
	int handlerTecho = sF->crearShader("src/techo.vert","src/techo.frag" );
	int handlerOjos = sF->crearShader("src/DiffuseShadingVShader.vert", "src/DiffuseShadingFShader.frag");
	int handlerReflection = sF->crearShader("src/normalReflectionMapping.vert", "src/normalReflectionMapping.frag");

	techo->setHandle(handlerTecho);
	cang->setHandle(nuevo);
	cang2->setHandle(nuevo);
	cang3->setHandle(nuevo);
	cang->setHandle2(handlerPhong);
	cang2->setHandle2(handlerPhong);
	cang3->setHandle2(handlerPhong);
	cang->setHandleOjos(handlerPlanta);
	cang2->setHandleOjos(handlerPlanta);
	cang3->setHandleOjos(handlerPlanta);
	hoyo1->setHandle(nuevo);
	hoyo2->setHandle(nuevo);
	hoyo3->setHandle(nuevo);
	hoyo4->setHandle(nuevo);
	suelo->setHandle(handlerPhong);
	alga1->setHandle(handlerWave);
	alga2->setHandle(handlerWave);
	alga3->setHandle(handlerWave);
	alga4->setHandle(handlerWave);
	alga5->setHandle(handlerWave);
	pez1->setHandle(handlerReflection);
	pez2->setHandle(handlerReflection);
	pez3->setHandle(handlerReflection);
	pez1->setHandlerOjos(handlerOjos);
	pez2->setHandlerOjos(handlerOjos);
	pez3->setHandlerOjos(handlerOjos);


}

void myWindow::OnResize(int w, int h)
{
    glViewport (0, 0, (GLsizei) w, (GLsizei) h);
    this->width = w;
    this->height = h;
}

void  myWindow::OnClose(void)
{
	cout << "cerrando" << endl;
	delete this->camara;
	for (unsigned int i = 0; i < this->listaRenders.size(); i++) delete this->listaRenders[i];
}

void myWindow::OnMouseDown(int button, int x, int y)
{
}

void myWindow::OnMouseUp(int button, int x, int y)
{
	if (button == MOUSE_LEFT_BUTTON) this->cameraDragging = false;
}

//! Called while Left Mouse button is pressed.
void myWindow::OnLeftMouseDrag(int x, int y){
	if(this->cameraDragging) {
		this->camara->rotarCamara(this->dy-y, this->dx-x);
		//std::cout<<"Llamada rotacion x: " << this->dx-x <<" Y:" << this->dy-y <<std::endl;
//		this->Repaint();
	}
	this->cameraDragging = true;
	this->dx = x;
	this->dy = y;
	return;
}

void myWindow::OnMouseWheel(int nWheelNumber, int nDirection, int x, int y)
{

}

void myWindow::OnKeyDown(int nKey, char cAscii)
{
	if (cAscii == 27) // 0x1b = ESC
	{
		for (unsigned int i = 0; i < this->listaRenders.size(); i++) this->listaRenders[i]->~Dibujable();
		cout << "cerrando" << endl;
		this->Close(); // Close Window!
	}
};

void myWindow::OnKeyUp(int nKey, char cAscii)
{
	switch (cAscii){
		case 'q':
		cout << "cerrando por este medio" << endl;
		for (unsigned int i = 0; i < this->listaRenders.size(); i++)
			{this->listaRenders[i]->~Dibujable();
			cout << i << endl;
			}
		this->Close(); // Close Window!
		break;
		case 'f':
			SetFullscreen(true);
			break;
		case 't':
			SetFullscreen(false);
			break;
		case 'b':
//			cang->moverAlaDerecha();
//			pez1->moverAletas();
			break;
		case 'v':
//				cang->moverAlaIzquierda();
//				pez1->moverPez();
//				cang2->moverAlaIzquierda();
				break;
		case 'w':
			//Zoom in
			this->camara->zoomIn();
			break;
		case 's':
			//Zoom out
			this->camara->zoomOut();
			break;
		case 'a':
//			this->camara->moverIzquierda();
			break;
		case 'c':
			this->camara->cambiarModo();
			break;
		case 'm':
			if (mover) mover = false;
			else mover = true;
			break;
	}
	this->Repaint();
}
